package be.myandroid.games;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;

import javax.microedition.khronos.opengles.GL10;

import be.myandroid.games.TouchManager.touchListener;

public class Game implements touchListener{
	private static final String TAG = "Game";
	private int countPushes;
	public List<ExpandingCircle> circles = new LinkedList<ExpandingCircle>();
	private List<ExpandingCircle> circlesExp = new LinkedList<ExpandingCircle>();
	private List<ExpandingCircle> temp = new LinkedList<ExpandingCircle>();
	private boolean hasPushed = false;
	private float[] pushPos = new float[2];
	private StateManager state = StateManager.getInstance();
	private GameLevel level;
	private boolean endgame = false;
	private PriorityQueue<GameEvent> events = new PriorityQueue<GameEvent>();
	
	public Game(){
		state.currentGame = this;
		TouchManager.getInstance().addListener(this);
		level = GameLevel.getInstance();
		init();
	}
	
	protected abstract class GameEvent implements Comparable<GameEvent>{
		protected abstract void execute();
		@Override
		public int compareTo(GameEvent another) {
			return 0;
		}
	}
	
	protected class resetEvent extends GameEvent{
		protected void execute(){
			if(countPushes > 0 ){
				circles.clear();
				circlesExp.clear();
				init();
				endgame = false;
				countPushes = 0;
			}
			state.gameStart();
		}
	}
	
	protected class nextLevelEvent extends GameEvent{
		protected void execute(){
			level.nextLevel();
			(new resetEvent()).execute();
		}
	}
	
	public void reset(){
		events.offer(new resetEvent());
	}
	
	public void nextLevel(){
		events.offer(new nextLevelEvent());
	}
	
	private void init(){
		ExpandingCircle.MAX_TIME = level.ExplTime;
		ExpandingCircle.MAXSIZE = level.ExplSize;
		
		for(int i = 0 ; i < level.newCircles ; i++) {
			ExpandingCircle circle = new ExpandingCircle();
			circle.setColor((float)Math.random(),(float)Math.random(),(float)Math.random());
			circles.add(circle);
		}
	}
	
	public void draw(GL10 gl) {
		gl.glDisable(GL10.GL_TEXTURE_2D);
		
		while(!events.isEmpty())
			events.poll().execute();
		
		if(hasPushed && circlesExp.isEmpty()) {
			hasPushed = false;
			ExpandingCircle selected = getSelectedCircle();
			
			if(selected != null) {
				//Log.d("Selected:", "x:" + selected.pos[0] + "y:" + selected.pos[1]);
				circles.remove(selected);
				circlesExp.add(selected);
			}
		}
		
		Collisions();
		Iterator<ExpandingCircle> itt = circles.iterator();
        while(itt.hasNext()) {
        	ExpandingCircle c = itt.next();
        	c.draw(gl);
    		if(!state.Paused())
    			c.move(1);
        }
        itt = null;
        
        itt = circlesExp.iterator();
        while(itt.hasNext()) {
        	ExpandingCircle c = itt.next();
        	c.draw(gl);
        	if(!state.Paused())
        		c.expand(1);
        }
        
        checkEndGame();
        
        itt = null;
	}

	private ExpandingCircle getSelectedCircle() {
		double prev = Double.POSITIVE_INFINITY;
		ExpandingCircle selected = null;
		for (ExpandingCircle c : circles) {
			double dist = c.getDistance(pushPos);
			if(dist < prev) {
				prev = dist;
				selected = c;
			}
		}
		return selected;
	}

	private void checkEndGame() {
		if(endgame && circlesExp.size() == 0 && !hasPushed && state.Running()) {
			boolean outcome = circles.size() <= level.remainingCircles;
			if(outcome)
				level.nextLevel();
			state.setOutcome(outcome);
		}
	}

	private void Collisions() {
		Iterator<ExpandingCircle> itt = circlesExp.iterator();
		Iterator<ExpandingCircle> itt2;
		
		while(itt.hasNext()) {
			ExpandingCircle exp = itt.next();
			if(exp.removed()) {
				itt.remove();
				continue;
			}
			
			itt2 = circles.iterator();
			while(itt2.hasNext()) {
				ExpandingCircle c = itt2.next();
				if( exp.isHit(c.pos) ) {
					itt2.remove();
					temp.add(c);
				}
			}
		}
		
		circlesExp.addAll(temp);
		temp.clear();
	}

	public void OnClick(float xPos, float yPos) {
		//Log.d("PUSH", "x:" + xPos + "y:" + yPos);
		if(!hasPushed && countPushes < level.MaxPushes && state.Running()) {
			countPushes++;
			if(!(countPushes < level.MaxPushes))
				endgame = true;
			hasPushed = true;
			this.pushPos[Circle.X] = xPos;
			this.pushPos[Circle.Y] = yPos;
		}
	}
}
